package com.aitesting;

import com.aitesting.common.*;
import com.aitesting.domain.*;
import com.aitesting.testcase.*;
import com.alibaba.fastjson.JSON;
import io.restassured.response.Response;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

@Slf4j
public class APIRunner {

    // 存放整个session变量
    private final Map<String, Object> session_variables;
    // 对外访问接口对象
    protected Config config;
    protected List<Step> steps;
    private HttpSession session;
    // 存储每个测试步骤数据
    @Getter
    private List<StepData> step_data;
    // 配置信息
    private TConfig tConfig;
    // 测试步骤数据
    private List<TStep> tSteps;

    public APIRunner() {
        steps = new ArrayList<>();
        session_variables = new HashMap<>();
    }

    /**
     * 取出TConfig和TSteps对象
     */
    public void init_tests() {
        this.tConfig = this.config.perform();
        this.tSteps = new ArrayList<TStep>();
        for (Step step : steps) {
            this.tSteps.add(step.perform());
        }
    }

    private void parse_config(TConfig config) {
        config.getVariables().putAll(this.session_variables);
        config.setName((String) Parser.parse_data(config.getName(), config.getVariables()));
        config.setBase_url((String) Parser.parse_data(config.getBase_url(), config.getVariables()));
    }

    private StepData run_step(TStep step) {
        StepData stepData = new StepData(step.getName());

        log.info("测试步骤名称：" + step.getName());

        String url = Parser.build_url(this.tConfig.getBase_url(), step.getRequest().getUrl());

        log.info("接口请求地址：" + url);
        long start_at = System.currentTimeMillis();
        // 解析upload上传文件信息
        // prepare_upload_step(step)
        Map<String, ?> request_map = step.getRequest().map();

        log.info("接口请求数据：" + request_map.toString());

        Map<String, Object> parsed_request_map = (Map<String, Object>) Parser.parse_data(request_map, step.getVariables());

        // 存入变量容器
        step.getVariables().put("request", parsed_request_map);
        // 发送请求
        Response response = session.request(url, step.getRequest().getMethod(), parsed_request_map);
        log.info("接口响应结果为：" + response.body().asString());

        // 定义一个响应对象
        ResponseObject responseObject = new ResponseObject(response);
        step.getVariables().put("response", responseObject);

        // 获取需要提取的数据
        Map<String, String> extractors = step.getExtract();
        // 提取从服务器端返回的数据
        Map<String, Object> extract_mapping = responseObject.extract(extractors);
        stepData.setExport(extract_mapping);
        Map<String, Object> variables_mapping = step.getVariables();
        variables_mapping.putAll(extract_mapping);

        // 验证
        List<Map<String, Object>> validators = step.getValidator();
        boolean session_success = false;
        try {
            responseObject.validate(validators, variables_mapping);
            session_success = true;
        } catch (AssertionError ex) {
            log.error(ex.getMessage());
        } finally {
            stepData.setSessionData(this.session.getSessionData());
            stepData.setSuccess(session_success);
        }
        // 统计消耗时间
        stepData.setDuration(System.currentTimeMillis() - start_at);
        return stepData;
    }

    private APIRunner run_testcase(TestCase testCase) {
        this.tConfig = testCase.getConfig();
        this.tSteps = testCase.getSteps();
        this.parse_config(this.tConfig);

        this.session = this.session != null ? this.session : HttpSession.getSession();
        // 保存每个执行步骤提取的数据信息
        Map<String, Object> extracted_variables = new HashMap<String, Object>();
        // 执行测试用例
        for (TStep step : this.tSteps) {

            // 合并当前步骤及上个步骤返回的提取参数
            step.setVariables(Utils.merge_variables(step.getVariables(), this.tConfig.getVariables()));
            step.setVariables(Utils.merge_variables(step.getVariables(), extracted_variables));
            // 解析variables
            try {
                step.setVariables(Parser.parse_variables_mapping(step.getVariables()));
            } catch (BaseException ex) {
                log.error(ex.getMessage());
            }
            StepData step_data = this.run_step(step);
            Map<String, Object> extract_mapping = step_data.getExport();
            extracted_variables.putAll(extract_mapping);
        }
        this.session_variables.putAll(extracted_variables);
        return this;
    }

    public APIRunner run() {
        this.init_tests();
        TestCase testCase = new TestCase(this.tConfig, this.tSteps);
        return this.run_testcase(testCase);
    }

    public APIRunner run(String path) {
        this.tSteps = new ArrayList<>();
        // 加载yaml文件
        Map<String, Object> mapTestCase = Loader.load_testcase_file(path);
        // 解析config
        if (mapTestCase.containsKey("config")) {
            String json_config = JSON.toJSONString(mapTestCase.get("config"));
            this.tConfig = JSON.parseObject(json_config, TConfig.class);
        }
        // 解析steps
        List<LinkedHashMap> tempSteps = (List<LinkedHashMap>) mapTestCase.get("steps");
        for (LinkedHashMap step : tempSteps) {
            // 创建TStep对象
            String json_step = JSON.toJSONString(step);
            TStep tStep = JSON.parseObject(json_step, TStep.class);

            // 创建TRequest对象
            Map mapRequest = (Map) step.get("request");
            String json_request = JSON.toJSONString(mapRequest);
            TRequest tRequest = JSON.parseObject(json_request, TRequest.class);

            if (mapRequest.containsKey("data")) {
                tRequest.setData((Map<String, Object>) mapRequest.get("data"));
            }
            if (mapRequest.containsKey("headers")) {
                tRequest.setHeaders((Map<String, String>) mapRequest.get("headers"));
            }
            if (mapRequest.containsKey("params")) {
                tRequest.setParams((Map<String, String>) mapRequest.get("params"));
            }
            if (mapRequest.containsKey("upload")) {
                tRequest.setUpload((Map<String, Object>) mapRequest.get("upload"));
            }
            if (mapRequest.containsKey("cookies")) {
                tRequest.setCookies((Map<String, String>) mapRequest.get("cookies"));
            }
            tStep.setRequest(tRequest);
            // 解析validators
            if (step.containsKey("validators")) {
                String json_validators = JSON.toJSONString(step.get("validators"));
                List validatorList = JSON.parseObject(json_validators, List.class);
                tStep.setValidator(validatorList);
            }
            // 解析extractor
            if (step.containsKey("extract")) {
                String json_extract = JSON.toJSONString(step.get("extract"));
                Map<String, String> mapExtract = JSON.parseObject(json_extract, Map.class);
                tStep.setExtract(mapExtract);
            }
            // 解析variables
            if (step.containsKey("variables")) {
                String json_variables = JSON.toJSONString(step.get("variables"));
                Map<String, Object> mapVariables = JSON.parseObject(json_variables, Map.class);
                tStep.setVariables(mapVariables);
            }
            this.tSteps.add(tStep);
        }
        TestCase testCase = new TestCase(this.tConfig, this.tSteps);
        return this.run_testcase(testCase);
    }

    public APIRunner run(List<ApiRequestData> apis) {
        // 解析ApiRequestData
        config = new Config(apis.get(0).getName()).variables(apis.get(0).getVariables()).base_url(apis.get(0).getBase_url());

        for (ApiRequestData api : apis) {
            RunRequest runRequest = new RunRequest(api.getStep_name());
            try {
                //通过反射,根据请求的方法名在RunRequest类中找到相应的方法对象
                Method requestMethod = runRequest.getClass().getDeclaredMethod(api.getMethod().toLowerCase(Locale.ROOT), String.class);
                //执行该方法
                StepWithOptionArgs stepWithOptionArgs = (StepWithOptionArgs) requestMethod.invoke(runRequest, api.getUrl());
                //填充请求参数
                stepWithOptionArgs
                        .with_params(api.getParams())
                        .with_cookies(api.getCookies())
                        .with_headers(api.getHeaders())
                        .with_data(api.getData());
//						.with_upload(api.getUpload());
                //提取需要保存的数据
                StepRequestExtraction extraction = stepWithOptionArgs.extract();
                for (String key : api.getExtract().keySet()) {
                    extraction.with_jsonpath(api.getExtract().get(key), key);
                }
                //提取断言数据
                StepWithValidation validation = extraction.validate();
                List<Map<String, Object>> validations = api.getValidator();
                for (Map<String, Object> assertMap : validations) {
                    for (String key : assertMap.keySet()) {
                        Method method = validation.getClass().getDeclaredMethod(Utils.get_uniform_comparator(key).getName(), String.class, Object.class, String.class);
                        //根据断言类型取出断言信息
                        List items = (List) assertMap.get(key);
                        //执行断言
                        method.invoke(validation, items.get(0), items.get(1), items.get(2));
                    }
                }
                this.steps.add(new Step(validation));
            } catch (NoSuchMethodException e) {
                log.error("请求的方法不存在");
            } catch (InvocationTargetException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return this.run();
    }
}
